//go:build !notmpfiles
// +build !notmpfiles

// Package collector 包含所有用于收集和导出系统指标的单独收集器。

package collector

// raidDiskErrorCollector 结构体定义了 RAID 磁盘错误收集器

import (
	"fmt"
	"log/slog"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/prometheus/client_golang/prometheus"
)

// raidDiskErrorCollector 结构体定义了 RAID 磁盘错误收集器
// desc 是 Prometheus 指标描述符
// logger 是日志记录器
type raidDiskErrorCollector struct {
	desc   *prometheus.Desc
	logger *slog.Logger
}

// 注册到页面
func init() {
	// 注册 RAID 磁盘错误收集器到页面
	registerCollector("raiddisk", defaultEnabled, NewRaidDiskErrorCollector)
}

// NewRaidDiskErrorCollector 创建一个新的 RAID 磁盘错误收集器
func NewRaidDiskErrorCollector(logger *slog.Logger) (Collector, error) {
	desc := prometheus.NewDesc(
		prometheus.BuildFQName(namespace, "raid", "disk_error_count"),
		"Number of RAID disk errors detected.",
		[]string{"disk", "errorType", "controller"}, nil,
	)
	return &raidDiskErrorCollector{
		desc:   desc,
		logger: logger,
	}, nil
}

// Update 实现 Collector 接口，返回检测到的 RAID 磁盘错误数量
func (c *raidDiskErrorCollector) Update(ch chan<- prometheus.Metric) error {
	// 使用 raidtool 获取 RAID 磁盘信息
	c.logger.Info("开始更新 RAID 磁盘错误指标")

	// 获取当前执行文件的路径
	execPath, err := os.Executable()
	if err != nil {
		c.logger.Error("获取执行文件路径出错", "error", err)
		return err
	}

	// 获取执行文件所在的目录
	execDir := filepath.Dir(execPath)

	// 构造 raidtool 的绝对路径
	raidtoolPath := filepath.Join(execDir, "bin", "raidtool")

	// 检查 raidtool 文件是否存在
	if _, err := os.Stat(raidtoolPath); os.IsNotExist(err) {
		c.logger.Error("raidtool 文件不存在", "path", raidtoolPath, "error", err)
		return fmt.Errorf("raidtool 文件不存在: %s", raidtoolPath)
	}

	output, err := exec.Command(raidtoolPath, "getPdList").Output()
	if err != nil {
		c.logger.Error("执行命令出错", "error", err)
		return err
	}

	// 将输出结果保存到文件
	logFile := filepath.Join(execDir, "disk_result.log")
	err = os.WriteFile(logFile, output, 0644)
	if err != nil {
		c.logger.Error("保存文件出错", "error", err)
		return err
	}

	// 检测并解析日志文件格式
	disks, err := c.detectLogFormat(logFile)
	if err != nil {
		// 记录解析日志文件失败的错误
		c.logger.Error("解析 RAID 日志文件失败", "error", err)
		return err
	}

	// 添加调试信息
	c.logger.Info("解析完成", "磁盘数量", len(disks))

	for i, disk := range disks {
		c.logger.Info("处理磁盘", "索引", i, "设备ID", disk.DeviceID, "错误计数", disk.ErrorCounts)

		// 遍历所有错误类型并生成指标
		for errorType, count := range disk.ErrorCounts {
			c.logger.Info("生成指标", "设备ID", disk.DeviceID, "错误类型", errorType, "计数", count)

			ch <- prometheus.MustNewConstMetric(
				c.desc,
				prometheus.GaugeValue,
				float64(count),
				disk.DeviceID,   // 将设备 ID 作为标签传递
				errorType,       // 将错误类型作为标签传递
				disk.Controller, // 将控制器作为标签传递
			)
		}
	}

	c.logger.Info("完成 RAID 磁盘错误指标更新")
	return nil
}

// DiskInfo 表示物理磁盘设备的信息
type DiskInfo struct {
	// DeviceID 设备标识符
	DeviceID string
	// Controller 控制器名称
	Controller string
	// ErrorCounts 错误计数映射，键为错误类型，值为计数
	ErrorCounts map[string]int
}

// detectLogFormat 检测日志文件格式并选择合适的解析函数
func (c *raidDiskErrorCollector) detectLogFormat(filename string) ([]DiskInfo, error) {
	data, err := os.ReadFile(filename)
	if err != nil {
		return nil, fmt.Errorf("读取日志文件失败: %w", err)
	}

	content := string(data)

	// 使用 Info 级别确保日志输出
	c.logger.Info("开始检测日志格式", "文件大小", len(content))

	// 根据文件内容检测日志格式
	switch {
	case strings.Contains(content, "Physical Device information"):
		// arcconf 日志格式
		c.logger.Info("检测到 Arcconf 日志格式")
		return ParseArcconfPdLog(filename, c.logger)
	case strings.Contains(content, "CLI Version") && strings.Contains(content, "Drive /c"):
		// storcli 日志格式
		c.logger.Info("检测到 StorCLI 日志格式")
		return ParseStorcliLog(filename, c.logger)
	case strings.Contains(content, "Enclosure Device ID") || strings.Contains(content, "Virtual Drive"):
		// MegaCLI 日志格式
		c.logger.Info("检测到 MegaCLI 日志格式")
		// 使用 MegaCLI 解析函数
		pdDisks, err := ParseMegaCLIPdLog(filename, c.logger)
		if err != nil {
			return nil, err
		}

		ldDisks, err := ParseMegaCLILdLog(filename, c.logger)
		if err != nil {
			return nil, err
		}

		// 合并物理磁盘和逻辑磁盘信息
		allDisks := append(pdDisks, ldDisks...)
		return allDisks, nil
	//case strings.Contains(content, "MegaRAID"):
	//	// raidtool 日志格式
	//	return c.parseRaidtoolLog(filename)
	default:
		// 默认使用 arcconf 格式
		c.logger.Info("使用默认的 Arcconf 日志格式")
		return ParseArcconfPdLog(filename, c.logger)
	}
}

// 在文件末尾添加辅助函数
func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
